(function($){
	
	/**
	 *	Простое дерево на основе разметки (вложенных списков)
	 *	Подключать плагин необходимо к элементу, оборачивающему дерево ul - $('div.tree').phormTree()
	 */
	
	$.fn.phormTree = function(options, callback) {
		
		var opts = $.extend({
			tags: { ul:'ul', li:'li', a:'a' },
			expanded: true,
			binded: 'li'
		}, options);
		
		var tree = $(this);
		
		/**
		 *	Инициализация дерева
		 */
		
		tree.initTree = function() {
			
			var t = tree.children('ul');
			
			// Добавляем стили на узлы дерева
	
			t.addClass('ui-phorm-tree');
			t.find('li:has(ul)').addClass('ui-phorm-tree-folder').addClass('ui-phorm-tree-collapsed');
				
			// По умолчанию отображаем нулевой и первый уровни и скрываем остальные
			if(opts.expanded) {
				t.find('ul ul').hide();
				t.children('li').not('ul li ul li').removeClass('ui-phorm-tree-collapsed').addClass('ui-phorm-tree-expanded');
			} else {
				t.find('ul').hide();
			}
			
			// Открываем активные ветки и выделяем последний активный элемент
			t.find('li.active > ul').show();
			t.find('li.active').removeClass('ui-phorm-tree-collapsed').addClass('ui-phorm-tree-expanded');
			t.find('li.active:last > a').addClass('ui-state-active');
			
			// Обработка одинарного клика по ссылке
			// Раскрываем ветку если она закрыта и возвращаем истину
				
			tree.find('li a').on('click',function(){
					
				var li = $(this).parent('li');
					
				if(li.hasClass('ui-phorm-tree-collapsed')) {
					li.removeClass('ui-phorm-tree-collapsed').addClass('ui-phorm-tree-expanded');
					li.children('ul').show('blind','fast');
				}
					
				tree.find('li a').removeClass('ui-state-active');
				$(this).addClass('ui-state-active');
					
				return true;
		
			// Обработка hover на ссылке
					
			}).hover(
				function(){
					$(this).addClass('ui-state-hover');
				},
				function(){
					$(this).removeClass('ui-state-hover');
				}
			);
				
			// Обработка одинарного клика по элементу списка
			// Раскрываем или закрываем ветку и возвращаем ложь
				
			tree.find(opts.binded).click(function(){
					
				if(opts.binded == 'li') {
					var li = $(this);
				} else {
					var li = $(this).parent('li');
				}
					
				if(li.find('a').hasClass('ui-state-hover')) {
						
					return true; // Подпорка, чтобы обрабатывался только клик по иконке (тег a вложен в li)
					
				} else {
						
					if(li.hasClass('ui-phorm-tree-expanded')) {
						li.removeClass('ui-phorm-tree-expanded').addClass('ui-phorm-tree-collapsed');
						li.children('ul').hide('blind','fast');
					} else {
						li.removeClass('ui-phorm-tree-collapsed').addClass('ui-phorm-tree-expanded');
						li.children('ul').show('blind','fast');
					}
						
					return false;
					
				}
					
			});
			
		}
		
		/**
		 *	Обновление данных дерева
		 */
		
		this.reload = function(url) {
			
			$.get(url, function(data) {
				tree.children('ul').replaceWith(data);
				tree.initTree();
			});
			
		};
			
		return this.each(function() {
			
			tree.initTree();

		});
		
	};
	
	
	/**
	 *	Таблица с данными DataGrid
	 *	Можно подключать на любой блочный элемент
	 */
	
	$.fn.phormGrid = function(options, callback) {
		
		var opts = $.extend({
			selectable: true,
			sortable: true,
			pagerBefore: true,
			pagerAfter: true,
			search: true,
			icons: siteopts.url+'/ui/phorm/icons',
			url: '',
			perPageOptions: ['10', '15', '20', '25', '30', '40', '50', '100'],
			lang: {perpage: 'Записей на странице:',
				   displaying: 'Показано с',
				   from: 'с',
				   to: 'по',
				   of: 'из',
				   items: 'записей',
				   first: 'Первая страница',
				   next: 'Следующая страница',
				   previous: 'Предыдущая страница',
				   last: 'Последняя страница',
				   reload: 'Обновить',
				   page: 'Страница',
				   columns: 'Колонки',
				   notfound: 'Не найдено ни одной записи',
				   search: 'Поиск',
				   searchSubmit: 'Найти',
				   searchClear: 'Очистить'
			},	
			onComplete: function(){},
			onLoad: function(){},
			forms: {
				'search': {}, // Объект для хранения значений поисковой формы	
				'pager':  {}  // Объект для хранения значений постраничной навигации и сортировок
			}
		}, options);
		
		var grid = $(this);
		
		/**
		 *	Инициализация таблицы
		 *	Загрузка данных таблицы черз JSON
		 */
		
		grid.initGrid = function(url,step) {
			
			$.getJSON(url, function(data){
				grid.html('<table class="ui-phorm-grid-table"/>');
				grid.setHeadings(data.headings);
				grid.setRows(data.rows, data.headings);
				if((opts.pagerBefore || opts.pagerAfter) && data.pager) grid.setPaging(data.pager);
				if(opts.search && data.search) grid.setSearch(data.search);
			},opts.onLoad(grid))
			.success(function(data){
				opts.onComplete(grid);
				// Установка значений при первой инициалитзации ui-phorm-grid-search
				if(step == 1 && opts.search) {
					grid.find('.ui-phorm-grid-search').find('input,select').each(function(i,item){
						if(mvcinfo[$(item).attr('name')]) {
							$(item).val(mvcinfo[$(item).attr('name')]);
						}
					});
				}
			}).error(function(data){
				console.log(data);
			});
			
		};
		
		/**
		 *	Отрисовка заголовков колонок
		 */
		
		grid.setHeadings = function(headings) {
			
			var thead = $('<thead/>').addClass('ui-state-active');
			
			$(headings).each(function(i,head){
				if(head.show!=0) {
					var width = head.width ? ' width="'+head.width+'"' : '';
					var th = $('<th'+width+'>'+head.label+'</th>');
					if(opts.sortable==true && head.sortable!=0) {
						th.css('cursor','pointer').on('click',function(){
							// Клик на заголовке/сортировка
							if($(this).hasClass('ui-phorm-grid-order-asc')) {
								thead.find('th').removeClass('ui-phorm-grid-order-asc');
								$(this).addClass('ui-phorm-grid-order-desc');
								opts.forms.pager.sortOrder = 'desc';
							} else {
								thead.find('th').removeClass('ui-phorm-grid-order-desc');
								$(this).addClass('ui-phorm-grid-order-asc');
								opts.forms.pager.sortOrder = 'asc';
							}
							opts.forms.pager.sortField = head.id;
							opts.forms.pager.page = 1; // При сортировках всегда начинаем вывод с первой страницы
							grid.reInit();
						});
						// Сортировка при инициализации
						if(opts.forms.pager.sortField && opts.forms.pager.sortField==head.id && opts.forms.pager.sortOrder) {
							th.addClass('ui-phorm-grid-order-' + opts.forms.pager.sortOrder);
						}
					}
				}
				thead.append(th);
			});
			grid.children('table').append(thead);
			
		};
		
		/**
		 *	Отрисовка строк
		 */
		
		grid.setRows = function(rows, headings) {
			
			var tbody = grid.children('table').append('<tbody/>');
			
			$(rows).each(function(i,row){
				var tr = $('<tr/>').addClass('ui-widget-content');
				$(headings).each(function(d,head){
					tr.append('<td>'+row[head.id]+'</td>');
				});
				// Добавляем событие на hover по строке
				tr.hover(
					function(){ $(this).addClass('ui-state-highlight'); },
					function(){ $(this).removeClass('ui-state-highlight'); }
				);
				tbody.append(tr);
			});
			
		};
		
		/**
		 *	Отрисовка постраничной навигации
		 */
		
		grid.setPaging = function(pager) {
			
			// Если ничего не найдено при первичной инициализации вместо таблицы рисуем 404-е сообщение
			if(pager.found==0 && $.param(opts.forms.search)=='') {
				grid.empty().html('<div class="ui-phorm-grid-notfound ui-state-error ui-corner-all"><span class="ui-icon ui-icon-alert"></span>'+opts.lang.notfound+'</div>');
				return;
			// Если ничего не найдено при последующих инициализациях, то переопределяем параметры постраничной навигации
			} else if(pager.found==0) {
				pager.pages = 1;
				pager.displayingStart = 0;
			}
			
			var table = $('<div><table><tbody><tr></tr></tbody></table></div>').addClass('ui-state-default ui-corner-all ui-phorm-grid-pager');
			var tr = table.find('tr');
			
			var icon_prev_class = pager.page==1 ? 'disabled' : 'default';
			var icon_next_class = pager.page>=pager.pages ? 'disabled' : 'default';
			
			tr.append('<td>'+opts.lang.perpage+'</td>');
			
				// Список выбора кол-ва записей на страницу
				var select = $('<select name="perPage" />');
				$(opts.perPageOptions).each(function(i,perpage){
					var selected = pager.perPage==perpage ? ' selected="selected"' : '';
					select.append('<option value="'+perpage+'"'+selected+'>'+perpage+'</option>');
				});
			
			tr.append($('<td />').append(select))
				.append('<td><img src="'+opts.icons+'/resultset_first.png" title="'+opts.lang.first+'" class="first ui-phorm-grid-icon ui-state-'+icon_prev_class+'" width="16" height="16" /></td>') // Первая
				.append('<td><img src="'+opts.icons+'/resultset_previous.png" title="'+opts.lang.previous+'" class="previous ui-phorm-grid-icon ui-state-'+icon_prev_class+'" width="16" height="16" /></td>') // Предыдущая
				.append('<td>'+opts.lang.page+'</td>');
			
				// Список выбора страницы
				var select = $('<select name="page" />');
				for(i=1;i<=pager.pages;i++) {
					var selected = pager.page==i ? ' selected="selected"' : '';
					select.append('<option value="'+i+'"'+selected+'>'+i+'</option>');
				}

			tr.append($('<td />').append(select))
				.append('<td>'+opts.lang.of+'</td>')
				.append('<td>'+pager.pages+'</td>')
				.append('<td><img src="'+opts.icons+'/resultset_next.png" title="'+opts.lang.next+'" class="next ui-phorm-grid-icon ui-state-'+icon_next_class+'" width="16" height="16" /></td>') // Следующая
				.append('<td><img src="'+opts.icons+'/resultset_last.png" title="'+opts.lang.last+'" class="last ui-phorm-grid-icon ui-state-'+icon_next_class+'" width="16" height="16" /></td>') // Последняя
				.append('<td><img src="'+opts.icons+'/arrow_refresh.png" title="'+opts.lang.reload+'" class="reload ui-phorm-grid-icon ui-state-default" width="16" height="16" /></td>'); // Обновить
				
			if(pager.found==0) {
				tr.append('<td>'+opts.lang.notfound+'</td>')
			} else {
				tr.append('<td>'+opts.lang.displaying+'</td>')
				  .append('<td>'+pager.displayingStart+'</td>')
				  .append('<td>'+opts.lang.to+'</td>')
				  .append('<td>'+pager.displayingEnd+'</td>')
				  .append('<td>'+opts.lang.of+'</td>')
				  .append('<td>'+pager.found+'</td>');
			}
			
			// Вешаем обработку hovera на иконках
			table.find('.ui-phorm-grid-icon.ui-state-default').hover(
				function() { $(this).addClass('ui-state-hover'); },
				function() { $(this).removeClass('ui-state-hover'); }
			);
			
			// Смена кол-ва записей на странице
			table.find('select[name=perPage]').change(function(){
				opts.forms.pager.perPage = $(this).val();
				opts.forms.pager.page = 1; // При смене кол-ва записей на страницу всегда начинаем вывод с первой страницы
				grid.reInit();
			});
			
			// Смена номера страницы
			table.find('select[name=page]').change(function(){
				opts.forms.pager.page = $(this).val();
				grid.reInit();
			});
			
			// Первая страница
			table.find('.first.ui-phorm-grid-icon.ui-state-default').click(function(){
				table.find('select[name=page]').val(1);
				opts.forms.pager.page = 1;
				grid.reInit();
			});
			
			// Предыдущая страница
			table.find('.previous.ui-phorm-grid-icon.ui-state-default').click(function(){
				table.find('select[name=page]').val(pager.page-1);
				opts.forms.pager.page = pager.page-1;
				grid.reInit();
			});
			
			// Следующая страница
			table.find('.next.ui-phorm-grid-icon.ui-state-default').click(function(){
				table.find('select[name=page]').val(pager.page+1);
				opts.forms.pager.page = pager.page+1;
				grid.reInit();
			});
			
			// Последняя страница
			table.find('.last.ui-phorm-grid-icon.ui-state-default').click(function(){
				table.find('select[name=page]').val(pager.pages);
				opts.forms.pager.page = pager.pages;
				grid.reInit();
			});
			
			// Обновление
			table.find('.reload.ui-phorm-grid-icon.ui-state-default').click(function(){
				grid.reInit();
			});
			
			if(opts.pagerBefore) table.insertBefore(grid.children('.ui-phorm-grid-table'));
			if(opts.pagerAfter) table.clone(true).insertAfter(grid.children('.ui-phorm-grid-table'));
			
		};
		
		
		/**
		 *	Отрисовка поисковой формы
		 */
		
		grid.setSearch = function(search) {

			var table = $('<div><table><tbody><tr></tr></tbody></table></div>').addClass('ui-state-default ui-corner-all ui-phorm-grid-search');
			var tr = table.find('tr');
			
			tr.append('<td><img src="'+opts.icons+'/magnifier.png" title="'+opts.lang.search+'" width="16" height="16" /></td>');
			
			$(search).each(function(i,field){

				tr.append('<td>'+field.label+'</td>');
				
				var dp_params = { changeMonth: true, changeYear: true, yearRange: 'c-10:c' }; // Параметры datePicker
				
				// Простое текстовое поле
				if(field.type == 'Text') {
					tr.append('<td><input type="text" class="ui-phorm-form-text ui-widget-content ui-corner-all" name="'+field.id+'" /></td>');
					
				// Простое текстовое поле (задумано как более короткое чем Text)
				} else if(field.type == 'Int') {
					tr.append('<td><input type="text" class="ui-phorm-form-int ui-widget-content ui-corner-all" name="'+field.id+'" /></td>');
					
				// Простое текстовое поле с выбором даты
				} else if(field.type == 'Date') {
					var dp_from   = $('<input type="text" class="ui-phorm-form-calendar ui-widget-content ui-corner-all" name="'+field.id+'_from" readonly="readonly" />');
					tr.append($('<td/>').append(dp_to.datepicker(dp_params)));
					
				// Простое текстовое поле с выбором диапазона дат
				} else if(field.type == 'DatesBetween') {
					var dp_from   = $('<input type="text" class="ui-phorm-form-calendar ui-widget-content ui-corner-all" name="'+field.id+'_from" readonly="readonly" />');
					var dp_to     = $('<input type="text" class="ui-phorm-form-calendar ui-widget-content ui-corner-all" name="'+field.id+'_to" readonly="readonly" />');
					
					tr.append('<td>'+opts.lang.from+'</td>')
					  .append($('<td/>').append(dp_from.datepicker(dp_params)))
					  .append('<td>'+opts.lang.to+'</td>')
					  .append($('<td/>').append(dp_to.datepicker(dp_params)));
				
				// Простое текстовое поле с выбором диапазона чисел
				} else if(field.type == 'IntsBetween') {
					tr.append('<td><input type="text" class="ui-phorm-form-int ui-widget-content ui-corner-all" name="'+field.id+'_from"/></td>')
					  .append('<td>&mdash;</td>')
					  .append('<td><input type="text" class="ui-phorm-form-int ui-widget-content ui-corner-all" name="'+field.id+'_to" /></td>');
				
				// Простой список
				} else if(field.type == 'Select' || field.type == 'SelectModule') {
					var select = $('<select name="'+field.id+'" class="ui-phorm-form-select ui-widget-content ui-corner-all" />')
						.append('<option value="">---</option>');
					$.each(field.optiondata,function(m,v){
						if($.isPlainObject(v)) {
							var optgroup = $('<optgroup label="'+m+'" />');
							$.each(v,function(g,h){
								optgroup.append('<option value="'+g+'">'+h+'</option>');
							});
							select.append(optgroup);
						} else {
							select.append('<option value="'+m+'">'+v+'</option>');
						}
					});
					tr.append($('<td />').append(select));
				
				// Простой список разделов
				} else if(field.type == 'SelectCategory') {
					var select = $('<select name="'+field.id+'" class="ui-phorm-form-select ui-widget-content ui-corner-all" />')
						.append('<option value="">---</option>');
					$.each(field.optiondata,function(m,v){
						var pad = v.clevel ? new Array(parseInt(v.clevel)+1).join('&nbsp;&nbsp;&nbsp;') : '';
						select.append('<option value="'+v.categoryid+'">'+pad+v.categoryname+'</option>');
					});
					tr.append($('<td />').append(select));

				}
				
			});
			
			// Установка и обработка смены значений в поисковой форме
			tr.find('input,select').each(function(i,item){
				$(item).on('change',function(){
					if($(this).val()=='') {
						delete opts.forms.search[$(this).attr('name')];
					} else {
						opts.forms.search[$(this).attr('name')] = $(this).val();
					}
					grid.reInit(); // submit form
				});
				// Установка значений после поиска
				if(opts.forms.search[$(item).attr('name')]) $(item).val(opts.forms.search[$(item).attr('name')]);
			});
			
			// Кнопка отправки поисковой формы
			var submit = $('<button class="ui-phorm-form-button ui-button ui-widget ui-state-default ui-corner-all">'+opts.lang.searchSubmit+'</button>')
				.on('click',function(){
					grid.reInit(); // submit form
				});
				tr.append($('<td />').append(submit));
				
			// Кнопка очистки поисковой формы
			var clear = $('<button class="ui-phorm-form-button ui-button ui-widget ui-state-default ui-corner-all">'+opts.lang.searchClear+'</button>')
				.on('click',function(){
					opts.forms.search = {}; // clear form
					opts.forms.pager.page = 1; // При очистке поисковой формы всегда начинаем вывод с первой страницы
					grid.reInit(); // submit form
				});
				tr.append($('<td />').append(clear));
			
			// Добавляем поисковую форму на таблицу
			if(opts.pagerBefore && grid.children('.ui-phorm-grid-pager')) {
				table.addClass('margin_space').insertBefore(grid.children('.ui-phorm-grid-pager:first'));
			} else {
				table.insertBefore(grid.children('.ui-phorm-grid-table'));
			}
			
		};
		
		
		/**
		 *	Собирает uri для отправки запроса
		 *	И отправляет запрос на инициализацию таблицы
		 */
		
		grid.reInit = function(step) {
			
			grid.find('.ui-phorm-grid-pager .reload.ui-phorm-grid-icon').attr('src', opts.icons + '/load.gif');

			var p_pager = $.param(opts.forms.pager);
			var p_search = $.param(opts.forms.search);
			
			var qs = /^[^\?]+(\?.*?)$/.exec(opts.url);
			var uri = qs && qs[1] && p_pager+p_search!='' ? opts.url + '&' : p_pager+p_search!='' ? opts.url + '?' : opts.url;
			
			if(p_pager!='' && p_search!='') {
				grid.initGrid(uri + p_pager + '&' + p_search,step);
			} else {
				grid.initGrid(uri + p_pager + p_search,step);
			}	
			
		};
		
		
		return this.each(function() {
			
			grid.addClass('ui-phorm-grid');
			grid.reInit(1);

		});
		
	};
	
	
	/**
	 *	Макеты внешнего вида
	 */
	
	$.fn.phormLayout = function(options) {
		
		var opts = $.extend({
			makets: {}, // Список макетов
			widgets: {}, // Список виджетов
			selected: false // Выбранный макет
		}, options);
		
		var layout = $(this).addClass('ui-phorm-layout');
		
		this.opts = opts;
		
		/**
		 *	Рисует панели и инициализируем JQuery Layout
		 */
			
		layout.setLayouts = function(){
			
			var west = $('<div class="ui-layout-west"/>').appendTo(layout);
			var center = $('<div class="ui-layout-center"/>').appendTo(layout);
			layout.layout({
				applyDefaultStyles: true,
				west__size: 240
			});
			return {
				makets: $('<div/>').addClass('ui-phorm-layout-makets').appendTo(west),
				workspace: $('<div/>').addClass('ui-phorm-layout-workspace').appendTo(center)
			};
			
		};
		
		
		/**
		 *	Рисует список макетов в левом слое
		 */
		
		layout.setMakets = function(panels){
			
			$.each(opts.makets,function(maketid,maket){
				
				var layout_maket = $('<div/>').addClass('ui-phorm-layout-maket').appendTo(panels.makets);
				var layout_sides = $('<div/>').addClass('ui-phorm-layout-sides ui-widget-content').appendTo(layout_maket);
				
				// Рисуем стороны в макетах
				$.each(maket.layouts,function(sideid,side){
					$('<div name="'+sideid+'">'+sideid+'</div>')
						.addClass('ui-phorm-layout-side ui-widget-content')
						.css('float','left')
						.css('width',side.width+'%')
						.css('height',side.height+'%')
						.appendTo(layout_sides);
				});
				$('<div/>').css('clear','both').appendTo(layout_sides);
				
				$('<div/>')
					.append($('<input type="radio" name="layoutname" value="'+maketid+'" />'))
					.append(maket.name)
					.appendTo(layout_maket);
				
				// Выбираем заданный в опциях макет
				if(opts.selected == maketid) layout.setSelected(panels,layout_maket);
				
				// Обработка клика на макете
				layout_maket.on('click',function(){
					layout.setSelected(panels,$(this));
				});
			
			});
			
		};
		
		
		/**
		 *	Выбор макета из списка макетов
		 */
		
		layout.setSelected = function(panels,maket){
			
			// Снимаем выделение со всех макетов
			panels.makets.find('.ui-phorm-layout-sides').removeClass('ui-state-active')
				  .find('.ui-phorm-layout-side').removeClass('ui-state-active');
			panels.makets.find('input[type=radio]').attr('checked',false);
					
			// Копируем выбранный макет без обработчиков событий и инициализируем рабочее пространство
			var layout_workspace = panels.workspace.empty().append(maket.children('.ui-phorm-layout-sides').clone());
			layout_workspace.find('.ui-phorm-layout-sides').width('100%').height('100%')
							.find('.ui-phorm-layout-side').empty().css('text-align','left');
			layout.setWorkspace(panels.workspace);
					
			// Выделяем текущий макет
			maket.children('.ui-phorm-layout-sides').addClass('ui-state-active')
				 .children('.ui-phorm-layout-side').addClass('ui-state-active');
			maket.find('input[type=radio]').attr('checked','checked');
			
		}
		
		
		/**
		 *	Инициализация и управление рабочим пространством в центральном слое
		 */
		
		layout.setWorkspace = function(panel){
			
			// Очищаем панель
			panel.find('.ui-phorm-layout-side').empty();
			
			// Кнопка добавления виджета
			var button_add = $('<a href="#" class="add ui-corner-all ui-button ui-state-default" title="Добавить виджет"><span class="ui-icon ui-icon-plus"></span></a>');
			button_add.on('click',function(){
				$('<iframe src="'+siteopts.url+'/control/widgets/dialog/layout/'+$(this).parent().attr('name')+'/" />')
					.phormDialog();
				return false;
			});
			panel.find('.ui-phorm-layout-side')
				.append(button_add)
				.append('<ul class="ui-phorm-layout-widgets"></ul>');
			
			// Раскидываем виджеты по панелям
			$.each(opts.widgets,function(sideid,widgets){
				
				var ul = panel.find('.ui-phorm-layout-side[name='+sideid+'] ul.ui-phorm-layout-widgets');
				
				$.each(widgets,function(i,widget){
					
					var li = $('<li><label>'+widget.widgetname+'</label></li>').addClass('ui-widget-content');
					
					// Кнопка редактирования виджета
					var button_edit = $('<a href="#" class="edit ui-corner-all ui-button ui-state-default" title="Редактировать виджет"><span class="ui-icon ui-icon-pencil"></span></a>').on('click',function(){
						$('<iframe src="'+siteopts.url+'/control/widgets/dialog/layout/'+sideid+'/position/'+i+'/?typeid='+widget.typeid+'"/>')
							.phormDialog();
						return false;
					});
					
					// Кнопка удаления виджета
					var button_delete = $('<a href="#" class="delete ui-corner-all ui-button ui-state-default" title="Удалить виджет"><span class="ui-icon ui-icon-close"></span></a>').on('click',function(){
						if(confirm('Удалить виджет?')) {
							li.remove();
							layout.setWidgets();
							return false;
						}
					});
					
					li.append('<textarea name="widgets['+sideid+'][]" class="hidden">'+$.toJSON(widget)+'</textarea>')
					  .append(button_edit)
					  .append(button_delete)
					  .appendTo(ul);
				
				});
			
			});
			
			// Сортировка виджетов
			panel.find('ul.ui-phorm-layout-widgets').sortable({ 
				connectWith: 'ul.ui-phorm-layout-widgets',
				placeholder: 'ui-state-highlight',
				receive: function(event,ui){
					var side = ui.item.parent('ul').parent('.ui-phorm-layout-side').attr('name');
					ui.item.find('textarea').attr('name','widgets['+side+'][]');
					layout.setWidgets();
					layout.setWorkspace(layout.panels.workspace);
				}
			}).disableSelection();
			
		}
		
		
		/**
		 *	Перестраивает массив объектов виджетов на основе данных в центральном рабочем слое
		 *	Необходимо дергать при добавлении/редактировании/удалении/перетаскивании виджетов
		 */
		
		layout.setWidgets = function(){
			
			$.each(layout.find('.ui-phorm-layout-workspace .ui-phorm-layout-side'),function(i,widget){
				var sideid = $(this).attr('name');
				if(!opts.widgets[sideid]) opts.widgets[sideid] = {};
				opts.widgets[sideid] = [];
				$.each($(this).find('textarea'),function(){
					opts.widgets[sideid].push($.evalJSON($(this).val()));
				});
			});
			
		}
		
		
		/**
		 *	Заполняет данными форму редактирования виджета, открываемую в диалоге phormDialog
		 *	Данные берутся из опций в зависимости от выбранного виджета в центральном рабочем пространстве
		 *	!!!Метод публичный - Дергается из Phorm_Controller_Module_Control_Widgets::dialogAction
		 *	@param string $sideid Название слоя в макете отображения
		 *	@param int $position Позиция виджета
		 *	@param object $form Объект формы 
		 */
		
		this.setWidgetValues = function(sideid,position,form){
			
			if(opts.widgets[sideid]) {
			
				var FormData = opts.widgets[sideid][position];
			
				for (var i in FormData) { 
					form.find("[name="+i+"],[name=\'"+i+"[]\']").each(function(){ 
						$(this).val($(this).attr("name").match(/\]$/) && FormData[i]!=null ? FormData[i].toString().split(",") : [FormData[i]]);
					});
				};
			
			}
			
		}
		
		
		/**
		 *	Обновляет данные виджета в рабочем пространстве после редактирования виджета в диалоге phormDialog
		 *	!!!Метод публичный - Дергается из Phorm_Controller_Module_Control_Widgets::dialogAction
		 *	@param string $sideid Название слоя в макете отображения
		 *	@param int $position Позиция виджета
		 *	@param string formdata Данные формы в виде JSON-строки
		 */
		
		this.getWidgetValues = function(sideid,position,formdata){
			
			if(position==-1) { // Добавление виджета (определяем позицию)
				
				if(!opts.widgets[sideid]) {
					opts.widgets[sideid] = {};
					position = 0;
				} else {
					position = opts.widgets[sideid].length;
				}
			
			}
			
			opts.widgets[sideid][position] = $.evalJSON(formdata);
			layout.setWorkspace(layout.panels.workspace);
			
			// ищем и закрываем диалог...
			phorm_ui_closeDialog();
			
		}

		return this.each(function() {
			
			var panels = layout.setLayouts();
			layout.panels = panels;
			layout.setMakets(panels);
			
		});
		
	};
	
	
	/**
	 *	Диалоговое окно
	 *	Подпорка для JQueryUI Dialog, чтобы вызывать диалог из ифрейма поверх родителя
	 *	Заодно добавим пару дефолтных параметров
	 *	Метод сквозной и работает также и без iframe (При использовании iframe убираем прокрутку - отдаем ее ифрейму)
	 */
	
	$.fn.phormDialog = function(options) {
		
		var opts = $.extend({
			modal: true,
			width: $(window.parent).width()*0.8,
			height: $(window.parent).height()*0.9,
			close: function(event,ui){
				$(this).dialog('destroy').remove();
				window.parent.$('.ui-dialog').remove(); // Подчищаем мусор после закрытия диалога
			}
		}, options);
		
		return this.each(function() {
			
			var selector = $(this).clone(true);
			var tagname = this.tagName.toLowerCase();
			
			var dlg = $('<div/>')
					.append('<input type="text" style="position:absolute;left:-300px;" />') // Подпорка для JQueryUI Dialog чтобы фокус не прыгал на первый элемент
					.append(selector);
			if(tagname=='iframe') dlg.css('overflow','hidden').css('padding',0);
			var dlg = window.parent.$(dlg).dialog(opts);
			
			// Растягиваем фрейм по ширине, высоте, убираем бордюр, вытаскиваем заголовок
			if(tagname=='iframe') {
				selector.css('width','100%').css('height','100%').css('border','none')
					.load(function(){
						if(!opts.title) {
							var h1 = $(this).contents().find('h1:first');
							dlg.dialog('option','title',h1.html());
							h1.remove();
						}
					});
			}

		});
		
	};
	
	
	/**
	 *	Географические координаты
	 *	При фокусе на текстовом поле открывается диалог с картой (возможно как календарь в DatePickere)
	 *	Если в поле уже есть координаты, то маркер ставится по этим координатам
	 *	Если координат нет, то определяем координаты пользователя по GeoLocation
	 *	Если в опциях указан поле-донор адреса, то на карте в геолокаторе оно подставляется автоматом
	 */
	
	$.fn.phormLatLong = function(options) {
		
		var opts = $.extend({
			provider: 'Yandex',
			apikey: false,
			width: 800,
			height: 600,
			center: [55.7514,37.6173],
			zoom: 10,
			icon: 'ui-icon-arrowrefresh-1-s', // Класс иконки, см. JQueryUI
			address: {} // Список id полей из которых склеивается адрес для геолокатора
		}, options);
		
		var latlong = $(this);
		var mapid = latlong.attr('id') + '_map';
		var maplayer = $('<div />').attr('id',mapid).width(opts.width).height(opts.height).hide().appendTo($('body'));
		var map = {}; // Объект карты
		var placeMark = {}; // Объект метки
		var searchControl = {}; // Объект поисковой формы
		
		/**
		 *	Приаттачиваем к полю иконку вызова карты
		 */
		
		var icon = $('<span style="margin:4px;"><a href="#" class="ui-corner-all ui-button ui-state-default" title="'+_('Выбрать на карте')+'"><span class="ui-icon ' + opts.icon + '"></span></a></span>').insertAfter(latlong);
		
		/**
		 *	Пытаемся автоматически определить координаты
		 */
		
		if(navigator.geolocation) {
			navigator.geolocation.getCurrentPosition(function(position){
				if(latlong.val() == '') {
					opts.center = [position.coords.latitude,position.coords.longitude];
					latlong.val(opts.center);
					opts.zoom = 16;
				}
			});
		}
		
		/**
		 *	Яндекс.Карты
		 */
		
		latlong.initYandex = function() {
			
			$.getScript('http://api-maps.yandex.ru/2.0-stable/?load=package.standard&lang=ru-RU', function(){
				icon.on('click',function(){

					maplayer.dialog({width: 800, height: 600, title: 'Укажите местоположение'});
					
					if(!map.container) {
						
						map = new ymaps.Map(mapid, {
							center: opts.center,
							zoom : opts.zoom
						});
						
						map.controls.add('zoomControl')
									.add('typeSelector');
						map.behaviors.enable('scrollZoom');
						
						/**
						 *	Метка
						 */
						
						placeMark = new ymaps.Placemark(map.getCenter(),{
							iconContent: 'Перетащи<br />меня'
						},{
							draggable: true,
							preset: 'twirl#blueStretchyIcon'
						});
						
						placeMark.events.add('drag', function(){
							latlong.val(placeMark.geometry.getCoordinates());
						});
						
						map.geoObjects.add(placeMark);
						
						/**
						 *	Поиск по адресу
						 */
						
						searchControl = new ymaps.control.SearchControl({
							presultsPerPage: 10, 
							width: 300, 
							noPlacemark: true 
						});
						
						searchControl.events.add('resultselect',function(event){
							searchControl.getResult(event.get('resultIndex')).then(function(result){
								var found = result.geometry.getCoordinates();
								placeMark.geometry.setCoordinates(found);
								latlong.val(found);
							});
						});
						
						map.controls.add(searchControl);
					
					} else {
						
						if(latlong.val() != '') {
							map.setCenter([
								parseFloat(latlong.val().split(',')[0]),
								parseFloat(latlong.val().split(',')[1])
							]);
							placeMark.geometry.setCoordinates(map.getCenter());
							searchControl.close();
						}
						
					}
					
					/**
					 *	Склейка адреса из полей формы для поиска
					 *	@todo Вынести в отдельный метод когда провайдеры начнут размножаться
					 */
					
					var address = [];
					
					$.each(opts.address,function(i,lmnt){
						lmnt = $('#' + lmnt);
						var val = '';
						if(lmnt) {
							switch(lmnt.get(0).tagName.toLowerCase()) {
								case 'select':
									val = lmnt.find(':selected').text();
									break;
								default:
									val = lmnt.val();
								break;
							}
							if(val!='') {
								address.push(val);
							}
						}
					});
					
					if(address.length) {
						searchControl.search(address.join(', '));
					}
					
				});
			});
			
		}
		
		return this.each(function() {
			
			if(opts.provider=='Yandex') {
				latlong.initYandex();
			}

		});
		
	};
	
	
	/**
	 *	Загрузчик медиа-файлов через Blueimp
	 *	Необходимо заранее подключить /ui/blueimp/js/jquery.fileupload.js
	 *	Плагин должен набрасываться непосредственно на input[type=file]
	 *	Инпут подменяется полем типа hidden с тем же именем, значением ставится mediaid 
	 *		если hidden уже есть СРАЗУ за инпутом, то используется он
	 */
	
	$.fn.phormMediaUploader = function(options) {
		
		var opts = $.extend({
			baseurl: '', // getBaseUrl() из контроллера или откуда там плагин дергается
			url: siteopts.url+'/control/media/upload/', // Урл на который сабмитятся файлы без указания типа (к нему автоматом добавится type/{type}/)
			type: 'image', // Тип загружаемых файлов
			imgsize: '48' // Размер отдаваемой картинки/иконки
		}, options);
		
		var uploader = $(this);
		
		/**
		 *	Устанавливает значение
		 *	Публичная функция
		 */
		
		this.setValue = function(item) { 
			return uploader.setValues(item,uploader); 
		};
		
		/**
		 *	Устанавливает значения
		 */
			
		uploader.setValues = function(item,input){
				
			if(typeof(item) == 'undefined') return false;
			if(!item.mediaid) return false;
			
			var icon = '<a href="#" class="delete ui-corner-all ui-button ui-state-default" title="Удалить"><span class="ui-icon ui-icon-close"></span></a>';
			
			if(opts.type=='image') {
				var src = opts.baseurl+'/uploads/media/'+item.filepath+'/'+item.mediaid+'_'+opts.imgsize+'x'+opts.imgsize+'_center.'+item.fileext;
				var img = $('<img src="'+src+'" width="'+opts.imgsize+'" height="'+opts.imgsize+'" title="'+item.filename+'" />').click(function(){
					$('<iframe src="'+opts.baseurl+'/uploads/media/'+item.filepath+'/'+item.mediaid+'.'+item.fileext+'" />').phormDialog({
						title:'Просмотр изображения'
					});
				});
			} else {
				//var img = $('<img src="'+opts.baseurl+'/ui/phorm/icons/ext/'+item.fileext+'.png" width="'+opts.imgsize+'" height="'+opts.imgsize+'" title="'+item.filename+'" />').click(function(){
				var src = 'http://cdn5.iconfinder.com/data/icons/fatcow/32x32/file_extension_'+item.fileext+'.png';
				var img = $('<img src="'+src+'" width="32" height="32" title="'+item.filename+'" />').click(function(){
					location.href = opts.baseurl+'/uploads/media/'+item.filepath+'/'+item.mediaid+'.'+item.fileext;
				});
			}
			
			var hidden = input.next('input[type=hidden]');
			if(hidden.length) {
				hidden.val(item.mediaid);
			} else {
				var hidden = $('<input type="hidden" />').attr('name',input.attr('name')).val(item.mediaid);
				hidden.insertAfter(input);
			}
			
			img.css('cursor','pointer').insertAfter(hidden);
			input.hide();
		        			
			$(icon).css('margin-left','4px').on('click',function(){
				if(confirm('Удалить?')) {
					var prnt = $(this).parent();
					prnt.find('img').remove();
					prnt.find('input[type=hidden]').val(0);
					prnt.find('input[type=file]').show().attr('data-mediainfo','');
					var fnl = prnt.find('input[type=file]').clone();
					prnt.find('input[type=file]').remove();
					prnt.append(fnl);
				    $(this).remove();
				}
				return false;
			}).insertAfter(img);
			
			return false;
			
		}
		
		return this.each(function() {
			
			$(this).fileupload({
		        dataType: 'json',
		        url: opts.url + 'type/' + opts.type + '/',
		        submit: function() {
		        	var loading = $('<div/>').css('width',$(this).width()).css('height',$(this).height()).addClass('ui-phorm-progress');
		        	loading.insertAfter($(this).hide());
		        },
		        done: function (e, data) {
		            var input = $(this);
		        	$.each(data.result, function (i, item) {
		            	if(item.errors) {
		                	var message = '';
		                	$.each(item.errors,function(rowid,rows){
		                		$.each(rows,function(errorid,errorstring){
		                			message += errorstring + "\n";
		                		});
		                	});
		                	input.show();
		                	input.next('.ui-phorm-progress').remove();
		                	input.before($('<div class="ui-phorm-warnings ui-state-error ui-corner-all"><ul><li>'+message+'</li></ul></div>'));
		                } else {
		                	input.next('.ui-phorm-progress').remove();
		                	uploader.setValues(item,input);
		                }
		            });
		        }
		    });
			
		});
		
	};

})(jQuery);


/**
 *	Дополнительные вспомогательные функции
 *	!!!Все функции должны иметь префикс phorm_ui_
 */

	/**
	 *	Обработка hovera на JQueryUI элементах
	 */
	
	function phorm_ui_changeState() {
		$('.ui-state-default').livequery(function(){
			$(this).not('.ui-phorm-grid-pager,.ui-phorm-grid-search').hover(
				function() { $(this).addClass('ui-state-hover'); },
				function() { $(this).removeClass('ui-state-hover'); }
			);
		});
	}
	
	
	/**
	 *	Автоматическое сокрытие сообщений об ошибках и подтверждений действий
	 *	Время задержки необходимо передавать в секундах. По умолчанию 5 сек
	 *	@param int timeout Время задержки (доступности сообщения) 
	 */
	
	function phorm_ui_hideWarnings(timeout) {
		if(!timeout) timeout = 5;
		$('.ui-phorm-warnings').livequery(function(){ 
			var $this = $(this);
			setTimeout(function(){ $this.hide('fade'); }, timeout*1000);
		});
	}
	
	/**
	 *	Закрывает диалог и вычищает мусор, который остается в разметке от JQuery UI Dialog
	 *	Вынесен отдельной функцией для возможности закрывать диалоги, вызванные напрямую через JQuery UI Dialog
	 */
	
	function phorm_ui_closeDialog() {
		
		window.parent.$('.ui-dialog-content').dialog('destroy').remove();
		window.parent.$('.ui-dialog').remove();
		return false;
		
	}

	/**
	 *	Выбор материала из грида для вставки в форму дочернего фрейма
	 *	Обеспечивает выбор связанных материалов, замещаемого материала и пр.
	 *	@param object btn Объект кнопки обзора материалов
	 *	@param int resourceid Id материала
	 *	@param string resourcename Название материала
	 *	@param object row Объект выбранной строки в гриде
	 */
	
	function phorm_ui_selectResource(btn,resourceid,resourcename,row) {
		
		var src = btn.next('ul').find('li:last');
		var trgt = src.clone(true).insertBefore(src).show();
		trgt.find('input').attr('disabled',false).val(resourceid);
		trgt.find('.title').html(resourcename);
		if(trgt.find('input').attr('name').match(/\]$/)) { // Проверяем, множественный выбор или нет
			row.addClass('ui-state-disabled');
		} else {
			trgt.prev().remove();
			phorm_ui_closeDialog();
		}
		return false;
		
	}
	
	
	/**
	 *	Выбор файла из файлового менеджера elFinder и вставка в редактор markItUp
	 *	Предполагается, что файлменеджер запущен в ифрейме в диалоге через phormDialog
	 *	@param string selector Id элемента редактора
	 *	@param string url Путь к файлу
	 *	@todo Перенести в плагин и сделать совместимость с TinyMCE, elRTE, CKEditor
	 */
	
	function phorm_ui_selectFileForMarkitup(selector,url) {
		
		var openWith = '';
		var closeWith = '';
		var replaceWith = '';
		
		var ext = url.split('.').pop().toLowerCase();
		
		// Картинки
		
		if(ext=='gif' || ext=='png' || ext=='jpg' || ext=='jpeg') {
			
			var img = new Image();
			img.src = url;
			
			img.onload = function() {
			
				replaceWith = '<img src="'+url+'" border="0" alt="" width="'+this.width+'" height="'+this.height+'" />';
				
				window.parent.frames["CenterFrame"].$.markItUp({
					openWith:function() { return openWith; },
					closeWith:function() { return closeWith; },
					replaceWith: function() { return replaceWith; },
					afterInsert: function() { phorm_ui_closeDialog(); }
				});
				
			}
			
		// Файлы
			
		} else {
			
			// Если выделение не пусто - ставим ссылку
			// если пусто - ставим урл
				
			window.parent.frames["CenterFrame"].$.markItUp({
				beforeInsert: function(h) { 
					if(h.selection==''){
						openWith = url;
					} else {
						openWith = '<a href="'+url+'">';
						closeWith = '</a>';
					}
				},
				openWith:function() { return openWith; },
				closeWith:function() { return closeWith; },
				replaceWith: function() { return replaceWith; },
				afterInsert: function() { phorm_ui_closeDialog(); }
			});
			
		}
		
		return false;
		
	}
	
	
	/**
	 *	Выбор файла из файлового менеджера elFinder и вставка в редактор TinyMCE
	 *	Здесь файлменеджер работает уже в TinyMCE Popup (в Phorm_Controller_Module_Control_Browser::filesAction должен быть подгружен tiny_mce_popup.js)
	 */
	
	function phorm_ui_selectFileForTinyMCE(selector,url) {

		var win = tinyMCEPopup.getWindowArg('window');
		
		win.document.getElementById(tinyMCEPopup.getWindowArg('input')).value = url;
		
		if (typeof(win.ImageDialog) != 'undefined') {
			// update image dimensions
			if (win.ImageDialog.getImageData) {
				win.ImageDialog.getImageData();
			}
			// update preview if necessary
			if (win.ImageDialog.showPreviewImage) {
				win.ImageDialog.showPreviewImage(URL);
			}
		}
		
		tinyMCEPopup.close();
		
		return false;
		
	}
	
	
	/**
	 *	Корректировка elFinder - делаем совместимым с UI Themeroller
	 *	Растягиваем на всю ширину окна
	 *	@todo Сделать плагин-прокси... как на диалоге
	 */
	
	function phorm_ui_correctElfinder(fm) {
		
		$('.el-finder-workzone').addClass('ui-widget-content');
		//$('.el-finder-toolbar').addClass('ui-state-default');
		//$('.el-finder-statusbar').addClass('ui-state-default');
		$('.el-finder-cwd').addClass('ui-widget-content');
		
		$.ajax().success(function(){
			setTimeout(function(){
				$('.el-finder-toolbar ul li').each(function(){
					if($(this).hasClass('delim')) {
						$(this).removeClass('delim').addClass('ui-icon ui-icon-grip-dotted-vertical').css('width','3px');
					} else {
						$(this).addClass('ui-widget-content ui-corner-all');
					}
				});
				//$('.el-finder-tree .selected').addClass('ui-state-active');
				var h = $('body').height()-$('h1').height()-$('.el-finder-statusbar').height()-$('.el-finder-toolbar').height()-40;
				$('.el-finder-nav').css('height',h);
				$('.el-finder-cwd').css('height',h);
			},500);
		});
		
	}
	
	